<!doctype html>
<html>
<head>
<script src="http://git123hub.gitee.io/exprcompiler/main.min.js"></script>
<style>
	a {text-decoration: none}
</style>
<title>类C语言的表达式编译器</title>
<meta charset="utf-8" />
</head>
<body>
	<center>
		<h1 style="font-size: 3em;">类 C 语言的表达式编译器</h1>
		<form>
			<input type="text" id="expr" style="width: 600px;" />
			<input type="button" value="编译" onclick="calcValue();" /><br />
			<textarea id="compiled" style="width:650px; height:400px;" readonly="readonly"></textarea><br />
			<input type="button" id="reinp" value="重新解释" onclick="reinterpret();" />
		</form>
	</center>
<script type="text/javascript">
// <![CDATA[
function $id (id) {
	return document.getElementById(id);
}
function calcValue () {
	var result = compileNumExpr($id("expr").value.replace(/\s/g,""));
	$id("compiled").value = result;
	$id("reinp").disabled = false;
}
function reinterpret () {
	var newstr = "", stackp = 0;
	var reg_used = [-1,-1,-1];
	var reg_name = "eax ecx edx".split(" ");
	var asmlist = $id("compiled").value.split(/\r?\n/g);
	var lastregs = [];
	var lastreg = 0;
	var top2 = 0, k0 = 0, k1 = 0, k2 = 0;
	var translate = {
		"Plus": "add ", "Minus": "sub ", "Multiply": "imul ",
		"BitwiseAnd": "and ", "BitwiseOr": "or ", "BitwiseXor": "xor ",
		"UnaryMinus": "neg ", "BitwiseNot": "not ",
		"BitwiseShl": "shl ", "BitwiseShr": "shr ",
	}
	for (var i, k = 0, l = asmlist.length; k < l; k++)
	{
		var pair = /^\s*(\w+)\s+(.*)\s*$/.exec(asmlist[k]);
		if (!pair) continue;
		// debugger;
		if (pair[1] === "push")
		{
			var min = 2e308;
			for (i = 0; i < 3; i++)
			{
				if (reg_used[i] < 0) break;
				if (min > reg_used[i]) {min = reg_used[i]; k1 = i;}
			}
			if (i >= 3)
			{
				i = k1;
				lastregs[reg_used[i]] = -1;
				newstr += "mov [esp" + (min?-min*4:"") + "], " + reg_name[i] + "\n";
			}
			newstr += "mov " + reg_name[i] + ", " + pair[2] + "\n";
			lastregs[stackp] = i;
			reg_used[i] = stackp++;
		}
		else if (pair[1] === "call")
		{
			if (pair[2] === "Plus" || pair[2] === "Minus" || pair[2] === "Multiply" ||
			    pair[2] === "BitwiseAnd" || pair[2] === "BitwiseOr" || pair[2] === "BitwiseXor") {
				// (reg1, reg2) => (reg)
				stackp--;
				if (lastregs[top2 = stackp-1] < 0)
					for (i = 0; i < 3; i++)
					{
						if (reg_used[i] < 0)
						{
							reg_used[i] = top2;
							lastregs[top2] = i;
							newstr += "mov " + reg_name[i] +", [esp" + (top2?-top2*4:"") + "]\n";
							break;
						}
					}
				newstr += translate[pair[2]] + reg_name[lastregs[top2]] + ", " + reg_name[lastregs[stackp]] + "\n";
				reg_used[lastregs[stackp]] = -1;
			}
			else if (pair[2] === "UnaryMinus" || pair[2] === "BitwiseNot")
				newstr += translate[pair[2]] + reg_name[lastregs[stackp-1]] + "\n";
			else if (pair[2] === "BitwiseShl" || pair[2] === "BitwiseShr") { // (reg1, reg2) => (reg3, cl) => (reg3)
				stackp--;
				k1 = lastregs[top2 = stackp-1];
				if (k1 < 0)
					for (i = 0; i < 3; i++)
					{
						if (reg_used[i] < 0)
						{
							reg_used[k1 = i] = top2;
							lastregs[top2] = i;
							newstr += "mov "+ reg_name[i] +", [esp" + (top2?-top2*4:"") + "]\n";
							break;
						}
					}
				if ((k0 = lastregs[stackp]) !== 1)
				{
					if (reg_used[1] >= 0)
					{
						newstr += "xchg ecx, " + reg_name[k0] + "\n";
						lastregs[reg_used[1]] = lastregs[stackp];
						k1 = k0;
					}
					else
					{
						newstr += "mov ecx, " + reg_name[k0] + "\n";
					}
					reg_used[k0] = reg_used[1];
				}
				newstr += translate[pair[2]] + reg_name[k1] + ", cl\n";
				reg_used[1] = -1;
			}
			else if (pair[2] === "Divide" || pair[2] === "Modulo") // (reg1, reg2) -> (edx:eax, ecx) => (eax, edx)
			{
				stackp--;
				for (i = 0; i < 3; i++)
				{
					k0 = reg_used[i];
					if (k0 >= 0 && k0 < stackp - 1)
					{
						newstr += "mov [esp" + (k0?-k0*4:"") + "], " + reg_name[i] + "\n";
						lastregs[k0] = -1;
						reg_used[i] = -1;
					}
				}
				k0 = lastregs[stackp-1];
				k1 = lastregs[stackp];
				if (k0 === 1)
				{
					if (k1 === 0)
					{
						newstr += "xchg eax, ecx\n";
					}
					else
					{
						newstr += "mov eax, ecx\n";
						newstr += "mov ecx, "+reg_name[k1]+"\n";
					}
				}
				else
				{
					var str = "";
					if (k0 < 0)
					{
						k2 = stackp-1;
						str = "[esp" + (k2?-4*k2:"") + "]";
					}
					else
						str = reg_name[k0];
					if (k0 !== 0) newstr += "mov eax, " + str + "\n";
					if (k1 !== 1) newstr += "mov ecx, " + reg_name[k1] + "\n";
				}
				newstr += "cdq\nidiv ecx\n";
				k2 = (pair[2] === "Divide") ? 0 : 2;
				reg_used[k2] = stackp-1;
				reg_used[1] = -1;
				reg_used[2-k2] = -1;
				lastregs[stackp-1] = k2;
			}
			else if (pair[2] === "LogicNot")
			{
				newstr += "test " + reg_name[lastregs[stackp-1]] + ", " + reg_name[lastregs[stackp-1]] +
				"\nsete " + reg_name[lastregs[stackp-1]].slice(1,2) + "l\nmovzx " +
				reg_name[lastregs[stackp-1]] + ", " + reg_name[lastregs[stackp-1]].slice(1,2) + "l\n";
			}
		}
	}
	$id("compiled").value = newstr;
	$id("reinp").disabled = true;
}
// ]]>
</script>
</body>
</html>